﻿using System.Collections;
using System.Collections.Generic;
using System.IO;

using UnityEngine;
using ILRuntime.CLR.TypeSystem;
using ILRuntime.CLR.Method;
using ILRuntime.CLR.Utils;
using ILRuntime.Runtime.Intepreter;
using ILRuntime.Runtime.Stack;
using ILRuntime.Runtime.Enviorment;
using LitJson;
using Mono.Cecil;

using Pumpkin.FrameBase;
using Pumpkin.Utils;

namespace Pumpkin.FrameBase.ILRTPlugins
{
    public class ILRTMain : MonoBehaviour , IModuleComponent
    {
        public static ILRTMain Ins;

        private static AppDomain appdomain_;
        private IFrameModule module_;

        protected virtual void Awake()
        {
            Ins = this;
        }

        public void SetModule(IFrameModule module)
        {
            module_ = module;
        }

        void Start()
        {
            StartCoroutine(LoadHotFixAssembly());
        }

        IEnumerator LoadHotFixAssembly()
        {
            appdomain_ = new ILRuntime.Runtime.Enviorment.AppDomain();
            ILRTUtils.SetDomain(appdomain_);

            string dllPath = HotFixConfigs.ScriptPath + ".bytes";
            var dllData = ResHelper.LoadAsset<TextAsset>(dllPath);
            if (dllData == null)
            {
                Log.LogError("Scripts cant be loaded");
                yield break;
            }
            byte[] dll = dllData.bytes;

            if (GlobalConfigs.Instance.IsDebugMode() && !GlobalConfigs.Instance.HotFixEnabled())
            {
                string pdbPath = FileHelper.GetABResFullPath(HotFixConfigs.ScriptPath + ".pdb");
                pdbPath = FileHelper.CombinePath(FileHelper.EditorProjectDir, pdbPath);
#if !UNITY_ANDROID
                pdbPath = "file:///" + pdbPath;
#endif
                WWW www = new WWW(pdbPath);
                yield return www;

                if (!string.IsNullOrEmpty(www.error))
                {
                    UnityEngine.Debug.LogError(www.error);
                }

                byte[] pdb = www.bytes;

                using (System.IO.MemoryStream fs = new MemoryStream(dll))
                {
                    using (System.IO.MemoryStream p = new MemoryStream(pdb))
                    {
                        appdomain_.LoadAssembly(fs, p, new Mono.Cecil.Pdb.PdbReaderProvider());
                    }
                }

            }
            else
            {
                using (System.IO.MemoryStream fs = new MemoryStream(dll))
                {
                    appdomain_.LoadAssembly(fs, null, null);
                }
            }

            InitializeILRuntime();
            OnHotFixLoaded();
        }

        void InitializeILRuntime()
        {
            RegisterDelegate();
            RegisterValueTypeBinder();

            //注册LitJson
            LitJson.JsonMapper.RegisterILRuntimeCLRRedirection(appdomain_);
            //最后进行CLR绑定
            //ILRuntime.Runtime.Generated.CLRBindings.Initialize(appdomain_);
        }

        void OnHotFixLoaded()
        {
            BaseCrossType.Init();
            Log.LogInfo("Init ILRuntime finish", LogColor.Yellow);
            module_.OnLoaded();
        }

        void Update()
        {

        }

        void RegisterValueTypeBinder()
        {
            appdomain_.RegisterValueTypeBinder(typeof(Vector3), new Vector3Binder());
            appdomain_.RegisterValueTypeBinder(typeof(Vector2), new Vector2Binder());
            appdomain_.RegisterValueTypeBinder(typeof(Quaternion), new QuaternionBinder());
        }

        void RegisterDelegate()
        {
            appdomain_.DelegateManager.RegisterMethodDelegate<System.String, System.Int32, System.Boolean, System.Object>();
            appdomain_.RegisterCrossBindingAdaptor(new CoroutineAdapter());
            appdomain_.DelegateManager.RegisterFunctionDelegate<System.Reflection.MethodInfo, System.Boolean>();

            appdomain_.DelegateManager.RegisterDelegateConvertor<KBEngine.NetworkInterfaceBase.ConnectCallback>((act) =>
            {
                return new KBEngine.NetworkInterfaceBase.ConnectCallback((ip, port, success, userData) =>
                {
                    ((System.Action<System.String, System.Int32, System.Boolean, System.Object>)act)(ip, port, success, userData);
                });
            });
        }

        public void DoCoroutine(IEnumerator coroutine)
        {
            StartCoroutine(coroutine);
        }
    }
}